python中的super()用法以及多继承协同任务

您所在的位置:网站首页 python super 多个父类 python中的super()用法以及多继承协同任务

python中的super()用法以及多继承协同任务

2022-03-23 15:13| 来源: 网络整理| 查看: 265

理解了python的MRO之后,我们就可以更加准确地使用super()函数,以及使用super()完成多继承协同任务

super().method()是调用父类中的方法,这个搜索顺序当然是按照MRO从前向后开始进行的

super([type][, object-or-type]) 根据官方文档,super函数返回一个委托类type的父类或者兄弟类方法调用的代理对象。super函数用来调用已经再子类中重写过的父类方法。

这句话其实很难看明白,为什么除了父类还可能是兄弟类?

要理解这句话,先谈谈super的参数的传入方式不同带来的不同之处

常见的是直接调用super(),这其实是super(type, obj)的简写方式,将当前的类传入type参数,同时将实例对象传入type-or-object参数,这两个实参必须确保isinstance(obj, type)为True。

使用该方法调用的super函数返回的代理类是obj所属类的MRO中,排在type之后的下一个父类。

示例: 类的继承结构如下

class A: pass class B: pass class C(A,B): pass

类C的MRO为[C, A, B, object]

现在我们为其添加一个方法x()

class A: def x(self): print('run A.x') super().x() print(self) class B: def x(self): print('run B.x') print(self) class C(A,B): def x(self): print('run C.x') super().x() print(self) C().x()

该方法最先出现是作为C的实例方法,根据MRO,我们很清楚,下一步它会调用其MRO父类中的同名方法,即A中的x()方法,但是,我们在A的x()方法中再次使用了super(),这时候会怎么样呢?

查看结果输出

run C.x run A.x run B.x

在调用了A中的x()方法之后,下一个调用的是B中的x()方法,在继承结构中,类A和类B互为兄弟关系,super()在A中调用的时候,最终却调用其兄弟的同名方法,这就是之前说的,super函数返回一个委托类type的父类或者兄弟类方法调用的代理对象。

那么,为什么? 根据print(self)的输出,所有在这些super()的调用过程中,self参数传入的是同一个obj,就是我们初始化的C(), 在内存中位置为0x000002B5041BB710的实例对象。

之前已经说过,super()是super(type, obj)的简写,在调用super()时,type参数传入的是当前的类,而obj参数则是默认传入当前的实例对象,在super()的后续调用中,obj一直未变,而实际传入的class是动态变化,不过,在首次调用时,MRO就已经被确定,是obj所属类(即C)的MRO,因此class参数的作用就是从已确定的MRO中找到位于其后紧邻的类,作为再次调用super()时查找该方法的下一个类。 即,super函数这一部分的核心逻辑应该为

def super(class, obj): mro_list = obj.__class__.mro() next_parent_class = mro_list[mro_list.index(class)+1] return next_parent_class

这就是为什么必须保证isinstance(obj, type)为True的原因,如果不是,那么可能type就不存在于obj.__class__的MRO列表中,该算法就无法正确找到下一个应当被查找的类。

因此,如果我们在某个类的父类中按照其MRO顺序,每个父类都写一个同名方法,同时每个该方法中都继续调用super(),直到在MRO列表object之前的最后一个类的同名方法中不再调用super(),那么在调用该方法时,会在各个父类中按照MRO列表的顺序依次被调用,这个过程中存在数据的传递,代表它们之间可以共享某些数据,这就实现了多继承协同工作。

而这种工作方式,通过重写方法是根本无法实现的。

使用实例:

继承结构如下图

我们试图达到的目的如下: 一个类Final继承Header以获得属性header 同时我们通过混合其他类来快捷地修饰header属性,例如继承类Mixin1会为header属性(其数据类型为列表)追加数据data1,而继承类Minix2则会为header属性的头部添加元素data2,注意,因为这些操作并不冲突,这些行为都不该相互覆盖。

class Minix1: """该混合类为header列表末尾添加data1""" def get_header(self): print('run Minix1.get_header') ctx = super().get_header() ctx.append('data1') return ctx class Minix2: """该混合类为header列表头部添加data2""" def get_header(self): print('run Minix2.get_header') ctx = super().get_header() ctx.insert(0, 'data2') return ctx class Header: header = [] def get_header(self): print('run Headers.get_header') return self.header if self.header else [] class Final(Minix1, Minix2, Header): def get_header(self): return super().get_header()

当然,我们可以定义更多的混合类,并从中选取所需的类来快速得到想要的header属性, 在这个例子中,这两个混合类已经足够说明问题。

我们现在使用类C的get_header()方法来得到其header属性

print(Final.mro()) #[Final, Minix1, Minix2, Header, object] header = Final().get_header() #run Minix1.get_header #run Minix2.get_header #run Headers.get_header print(header) #['data2', 'data1']

看来,运行得很成功,我们实现了多继承协同工作的目标,通过混合不同个类,来模块化地快速得到想要的header属性。 而这种工作方法,通过单纯的重写某个方法根本无法实现的,因为重写任何方法,它会在MRO列表中找到最优先(也就是最靠前)的拥有同名方法的类,然后调用该方法,并且终止检索,某项属性仅仅会被一个方法所影响。

相关文章或参考: Python进阶-继承中的MRO与super python 继承与多重继承



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3